package me.yuzho.study.concurrency.waxomatic2;

import static net.mindview.util.Print.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** Using explicit Lock and Condition Objects
 * 
 *  显式地使用lock和condition来实现线程之间的协作
 *  比较condition上的await，singalAll 与 object 上wait, notifyAll
 *  这个解决方案只有在更加困难的多线程问题中才是必需的
 * @author yzhou-citrix
 *
 */

class Car {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    
    private boolean waxOn = false;
    
    public void waxed() {
        lock.lock();
        try {
            waxOn = true; // ready to buffer
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    
    public void buffed() {
        lock.lock();
        try {
            waxOn = false; // ready to another coat of wax
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    
    
    public void waitForWaxing() throws InterruptedException {
        lock.lock();
        try {
            while (!waxOn) {
                condition.await();
            }
        } finally {
            lock.unlock();
        }
    }
    
    public void waitForBuffing() throws InterruptedException {
        lock.lock();
        try {
            while (waxOn) {
                condition.await();
            }
        } finally {
            lock.unlock();
        }
    } 
}

class WaxOn implements Runnable {
    private Car car;
    public WaxOn(Car c) { car = c; }
    @Override
    public void run() {
        try {
            
            while(!Thread.interrupted()) {
                printnb("Wax On!");
                TimeUnit.MILLISECONDS.sleep(200);
                car.waxed();
                car.waitForBuffing();
            }
            
        } catch (InterruptedException e) {
            print("Exiting via interrupt");
        }
        
        print("Ending Wax On task");
    }
}

class WaxOff implements Runnable {
    private Car car;
    public WaxOff(Car c) { car = c; }
    @Override
    public void run() {
        try {
            
            while(!Thread.interrupted()) {
                car.waitForWaxing();
                printnb("Wax Off!");
                TimeUnit.MILLISECONDS.sleep(200);
                car.buffed();
            }
            
        } catch (InterruptedException e) {
            print("Exiting via interrupt");
        }
        
        print("Ending Wax Off task");
    }
}

public class WaxOMatic2 {

    public static void main(String[] args) throws InterruptedException {
        Car car = new Car();
        
        ExecutorService exec = Executors.newCachedThreadPool();
        
        exec.execute(new WaxOff(car));
        exec.execute(new WaxOn(car));
        
        TimeUnit.SECONDS.sleep(5);
        
        exec.shutdownNow();
    }
}
